﻿using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.IO;
using System.Xml.Linq;

namespace BuildTasks
{
    public class CreateItemTemplateTask : Task
    {

        #region Input parameters

        /// <summary>
        /// Input paths (file name)
        /// </summary>
        [Required]
        public ITaskItem[] From { get; set; }

        /// <summary>
        /// Copy paths (file names)
        /// </summary>
        [Required]
        public ITaskItem[] Copy { get; set; }

        /// <summary>
        /// Replace strings
        /// </summary>
        public ITaskItem[] Replace { get; set; }

        /// <summary>
        /// Output file
        /// </summary>
        [Required]
        public ITaskItem OutputFile { get; set; }

        public ITaskItem DefaultName { get; set; }
        public ITaskItem Name { get; set; }
        public ITaskItem Description { get; set; }
        public ITaskItem ProjectType { get; set; }
        public ITaskItem SortOrder { get; set; }
        public ITaskItem Icon { get; set; }
        public ITaskItem TemplateGroupID { get; set; }
        public ITaskItem ShowByDefault { get; set; }
        public ITaskItem RequiredFrameworkVersion { get; set; }
        public ITaskItem NumberOfParentCategoriesToRollUp { get; set; }
        public ITaskItem[] References { get; set; }

        #endregion

        /// <summary>
        /// Execute task
        /// </summary>
        /// <returns>True/false</returns>
        public override bool Execute()
        {
            // Create temporary path
            string tempPath = Path.Combine(Path.GetTempPath(), "CreateItemTemplateTask");
            if (Directory.Exists(tempPath))
            {
                Log.LogMessage(string.Format("Deleting existing temporary path {0}...", tempPath));
                Directory.Delete(tempPath, true);
                Log.LogMessage(string.Format("Deleted existing temporary path {0}.", tempPath));
            }
            Log.LogMessage(string.Format("Creating temporary path {0}...", tempPath));
            Directory.CreateDirectory(tempPath);
            Log.LogMessage(string.Format("Created temporary path {0}", tempPath));


                // Add files
                foreach (ITaskItem item in From)
                {
                    string[] fileName = item.ItemSpec.Split(new string[1] { "=>" }, System.StringSplitOptions.RemoveEmptyEntries);

                    Log.LogMessage(string.Format("Processing {0}...", fileName[0]));

                    // Read file
                    TextReader tr = new StreamReader(fileName[0]);
                    string fileContents = tr.ReadToEnd();
                    tr.Close();

                    // Replace parameters
                    if (Replace != null && Replace.Length > 0)
                    {
                        foreach (ITaskItem replaceString in Replace)
                        {
                            string[] replacements = replaceString.ItemSpec.Split(new string[1] { "=>" }, System.StringSplitOptions.RemoveEmptyEntries);
                            fileContents = fileContents.Replace(replacements[0], replacements[1]);
                        }
                    }

                    // Write file
                    TextWriter tw = new StreamWriter(Path.Combine(tempPath, Path.GetFileName(fileName[0])));
                    tw.Write(fileContents);
                    tw.Close();

                    // Finish processing
                    Log.LogMessage(string.Format("Processed {0}.", fileName[0]));
                }

                // Copy files
                if (Copy != null && Copy.Length > 0)
                {
                    foreach (ITaskItem sourceFile in Copy)
                    {
                        Log.LogMessage(string.Format("Copying {0}...", sourceFile.ItemSpec));
                        File.Copy(sourceFile.ItemSpec, Path.Combine(tempPath, Path.GetFileName(sourceFile.ItemSpec)));
                        File.SetAttributes(Path.Combine(tempPath, Path.GetFileName(sourceFile.ItemSpec)), FileAttributes.Normal);
                        Log.LogMessage(string.Format("Copied {0}.", sourceFile.ItemSpec));
                    }
                }

                // Generate template XML
                Log.LogMessage("Generating template XML...");
                XNamespace xmlns = "http://schemas.microsoft.com/developer/vstemplate/2005";

                XElement itemReferences = new XElement(xmlns + "References");
                foreach (ITaskItem reference in References)
                {
                    itemReferences.Add(
                        new XElement(xmlns + "Reference",
                            new XElement(xmlns + "Assembly",
                                new XText(reference.ItemSpec)
                            )
                        )
                    );
                }

                XElement templateContent = new XElement(xmlns + "TemplateContent", itemReferences);
                foreach (ITaskItem item in From)
                {
                    string[] fileName = item.ItemSpec.Split(new string[1] { "=>" }, System.StringSplitOptions.RemoveEmptyEntries);
                    templateContent.Add(new XElement(xmlns + "ProjectItem",
                                    new XAttribute("TargetFileName", fileName[1]),
                                    new XAttribute("ReplaceParameters", true),
                                    new XText(Path.GetFileName(fileName[0]))
                                )
                    );
                }

                XDocument template = new XDocument(
                    new XElement(xmlns + "VSTemplate", new XAttribute("Version", "2.0.0"),
                                                       new XAttribute("Type", "Item"),
                        new XElement(xmlns + "TemplateData",
                            new XElement(xmlns + "DefaultName", DefaultName.ItemSpec),
                            new XElement(xmlns + "Name", Name.ItemSpec),
                            new XElement(xmlns + "Description", Description.ItemSpec),
                            new XElement(xmlns + "ProjectType", ProjectType.ItemSpec),
                            new XElement(xmlns + "SortOrder", SortOrder.ItemSpec),
                            new XElement(xmlns + "Icon", Icon.ItemSpec),
                            new XElement(xmlns + "TemplateGroupID", TemplateGroupID.ItemSpec),
                            new XElement(xmlns + "ShowByDefault", ShowByDefault.ItemSpec),
                            new XElement(xmlns + "RequiredFrameworkVersion", RequiredFrameworkVersion.ItemSpec),
                            new XElement(xmlns + "NumberOfParentCategoriesToRollUp", NumberOfParentCategoriesToRollUp.ItemSpec)
                        ),

                        templateContent
                    )
                );

                Log.LogMessage("Adding MyTemplate.vstemplate...");
                template.Save(Path.Combine(tempPath, "MyTemplate.vstemplate"));
                Log.LogMessage("Added MyTemplate.vstemplate.");

            // Create output ZIP file
            string outputPath = OutputFile.ItemSpec;
            if (File.Exists(outputPath))
            {
                Log.LogMessage(string.Format("Deleting existing {0}...", outputPath));
                File.Delete(outputPath);
                Log.LogMessage(string.Format("Deleted existing {0}.", outputPath));
            }
            Log.LogMessage(string.Format("Creating {0} from {1}...", outputPath, tempPath));
            FastZip fastZip = new FastZip();
            fastZip.CreateZip(outputPath, tempPath, true, "", "");
            Log.LogMessage(string.Format("Created {0}.", outputPath));

            // Remove temporary path
            if (Directory.Exists(tempPath))
            {
                Log.LogMessage(string.Format("Deleting temporary path {0}...", tempPath));
                Directory.Delete(tempPath, true);
                Log.LogMessage(string.Format("Deleted temporary path {0}.", tempPath));
            }

            // Success!
            return true;
        }
    }
}
